[Terraform リファクタリング]リソース内で定義していた設定を別リソースに移す
Terraformを使っていると、実リソースへの影響なくリソース内で定義した設定を別リソースに移したくなることが有ると思います。
例: リソースaws_instance
内でebs_block_device
を定義していたが、EBSの定義はリソースaws_ebs_volume
に移したい等
以下の手順で実現可能です。
- リファクタリングする
- 新規に記述を追加したリソースをインポートする
本ブログでは、サンプルコードを交えつつ上記の作業をやってみます。
やってみた
Provider Updateの対応手順ですが、公式ドキュメントには以下の手順がありました。
リソースaws_s3_bucket
内で定義したacl
をリソースaws_s3_bucket_acl
にリファクタリングするという内容です。
S3をリファクタリングしたい場合は、上記を参考にしてください。
全く同じでは面白くないので、今回はaws_instance
内のebs_block_device
をリソースaws_ebs_volume
に移す作業を例にやっていきます。
修正前のコード
EC2インスタンスを定義しています。
resource "aws_instance" "this" { ami = data.aws_ami.this.id instance_type = "t4g.nano" key_name = "sato-test" root_block_device { volume_type = "gp3" volume_size = "10" } ebs_block_device { device_name = "/dev/sdf" volume_type = "gp3" volume_size = "20" } tags = { Name = "refactor-test" } }
ハイライト箇所を置き換えます。
コードを修正する
ebs_block_device
の記述を削除して、リソースaws_ebs_volume
とaws_volume_attachment
を追加しました。
resource "aws_instance" "this" { ami = data.aws_ami.this.id availability_zone = "ap-northeast-1a" instance_type = "t4g.nano" key_name = "sato-test" root_block_device { volume_type = "gp3" volume_size = "10" } - ebs_block_device { - device_name = "/dev/sdf" - volume_type = "gp3" - volume_size = "20" - } tags = { Name = "refactor-test" } } +resource "aws_ebs_volume" "this" { + availability_zone = "ap-northeast-1a" + size = "20" + type = "gp3" +} +resource "aws_volume_attachment" "this" { + device_name = "/dev/sdf" + volume_id = aws_ebs_volume.this.id + instance_id = aws_instance.this.id +}
planを実行してみます。
$ terraform plan data.aws_ami.this: Reading... data.aws_ami.this: Read complete after 0s [id=ami-055b7614d7fc0f105] aws_instance.this: Refreshing state... [id=i-05aaeb83023a23766] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_ebs_volume.this will be created + resource "aws_ebs_volume" "this" { + arn = (known after apply) + availability_zone = "ap-northeast-1a" + encrypted = (known after apply) + final_snapshot = false + id = (known after apply) + iops = (known after apply) + kms_key_id = (known after apply) + size = 20 + snapshot_id = (known after apply) + tags_all = (known after apply) + throughput = (known after apply) + type = "gp3" } # aws_volume_attachment.this will be created + resource "aws_volume_attachment" "this" { + device_name = "/dev/sdf" + id = (known after apply) + instance_id = "i-05aaeb83023a23766" + volume_id = (known after apply) } Plan: 2 to add, 0 to change, 0 to destroy.
追加したリソースaws_ebs_volume
とaws_volume_attachment
が差分として表示されます。
Stateファイルを見てみると、resource aws_instance
内でEBSボリュームの定義があります。
{ "mode": "managed", "type": "aws_instance", "name": "this", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", "instances": [ { "schema_version": 1, "attributes": { # 省略 "ebs_block_device": [ { "delete_on_termination": true, "device_name": "/dev/sdf", "encrypted": false, "iops": 3000, "kms_key_id": "", "snapshot_id": "", "tags": {}, "tags_all": {}, "throughput": 125, "volume_id": "vol-XXXXXX", "volume_size": 20, "volume_type": "gp3" } ],
今回の変更内容は、resourceaws_ebs_volume
とaws_volume_attachment
を追加します。
Terraformは追加したリソースが既存のリソースであることを知らないため、差分が表示されます。
この状態でapply
すると、EBSボリュームを新規に追加してアタッチしようします。
しかし、/dev/sdf
には既存のEBSボリュームがアタッチされているので、エラーになります。
エラーや実リソースへの影響なくリファクタリングするには、追加した記述が既存リソースのものであることを、Terraformに教える必要があります。
Terraformはtfstateで状態を管理するため、tfstateに記述を追加します。
import
を行うことで、既存リソースをtfstateに取り込むことができます。
新規に記述を追加したリソースをインポートする
インポート対象は、先程のPlanで差分が出たaws_ebs_volume
とaws_volume_attachment
です。
インポートブロック又は、インポートコマンドでインポートを行います。(どちらか1つでOK)
インポートコマンド
$ terraform import aws_ebs_volume.this vol-XXXXXX # ボリュームID $ terraform import aws_volume_attachment.this /dev/sdf:vol-XXXXXX:i-YYYYYYY # デバイス名:ボリュームID:インスタンスID
インポートブロック
import { to = aws_ebs_volume.this id = "vol-XXXXXX" # ボリュームID } import { to = aws_volume_attachment.this id = "/dev/sdf:vol-XXXXXX:i-YYYYYYY" # デバイス名:ボリュームID:インスタンスID }
- aws_ebs_volume | Resources | hashicorp/aws | Terraform | Terraform Registry
- aws_volume_attachment | Resources | hashicorp/aws | Terraform | Terraform Registry
tfstateを確認すると、aws_ebs_volume
とaws_volume_attachment
が追加されたことがわかります。
{ "mode": "managed", "type": "aws_ebs_volume", "name": "this", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", # 省略 { "mode": "managed", "type": "aws_volume_attachment", "name": "this", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", # 省略 }
Planで差分がでないことを確認
再度Planを実行します。
% terraform plan aws_ebs_volume.this: Refreshing state... [id=vol-XXXXXX] data.aws_ami.this: Reading... data.aws_ami.this: Read complete after 0s [id=ami-055b7614d7fc0f105] aws_instance.this: Refreshing state... [id=i-YYYYYYY] aws_volume_attachment.this: Refreshing state... [id=vai-XXXXXX] No changes. Your infrastructure matches the configuration. Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
No changes.
になっており、差分がでていません。実リソースへの影響なく、リファクタリングができました。
おわりに
リソース内で定義していた設定を別リソースに移す作業についてでした。
破壊的な変更を含むAWS Providerのバージョンアップや、Terraformの記述を統一したいときなどに使える方法です。
リファクタリングの参考になれば幸いです。
以上、AWS事業本部の佐藤(@chari7311)でした。